home *** CD-ROM | disk | FTP | other *** search
/ Nautilus 1992 July / Nautilus-3-8 / Nautilus-3-8.bin / Tools & Utilities / Techy Stuff / Source ƒ / egrep-1.5 / grep-src / grep.c < prev    next >
Encoding:
Text File  |  1991-10-20  |  37.2 KB  |  1,330 lines

  1. /*  This file has been changed-- it is not the standard FSF version.
  2.  
  3.                          Oct 1991.  */
  4.  
  5.  
  6. /* grep - print lines matching an extended regular expression
  7.    Copyright (C) 1988 Free Software Foundation, Inc.
  8.                       Written June, 1988 by Mike Haertel
  9.                   BMG speedups added July, 1988
  10.             by James A. Woods and Arthur David Olson
  11.  
  12.                NO WARRANTY
  13.  
  14.   BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
  15. NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
  16. WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
  17. RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
  18. WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
  19. BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  20. FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
  21. AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
  22. DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
  23. CORRECTION.
  24.  
  25.  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
  26. STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
  27. WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
  28. LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
  29. OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
  30. USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
  31. DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
  32. A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
  33. PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
  34. DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
  35.  
  36.         GENERAL PUBLIC LICENSE TO COPY
  37.  
  38.   1. You may copy and distribute verbatim copies of this source file
  39. as you receive it, in any medium, provided that you conspicuously and
  40. appropriately publish on each copy a valid copyright notice "Copyright
  41.  (C) 1988 Free Software Foundation, Inc."; and include following the
  42. copyright notice a verbatim copy of the above disclaimer of warranty
  43. and of this License.  You may charge a distribution fee for the
  44. physical act of transferring a copy.
  45.  
  46.   2. You may modify your copy or copies of this source file or
  47. any portion of it, and copy and distribute such modifications under
  48. the terms of Paragraph 1 above, provided that you also do the following:
  49.  
  50.     a) cause the modified files to carry prominent notices stating
  51.     that you changed the files and the date of any change; and
  52.  
  53.     b) cause the whole of any work that you distribute or publish,
  54.     that in whole or in part contains or is a derivative of this
  55.     program or any part thereof, to be licensed at no charge to all
  56.     third parties on terms identical to those contained in this
  57.     License Agreement (except that you may choose to grant more extensive
  58.     warranty protection to some or all third parties, at your option).
  59.  
  60.     c) You may charge a distribution fee for the physical act of
  61.     transferring a copy, and you may at your option offer warranty
  62.     protection in exchange for a fee.
  63.  
  64. Mere aggregation of another unrelated program with this program (or its
  65. derivative) on a volume of a storage or distribution medium does not bring
  66. the other program under the scope of these terms.
  67.  
  68.   3. You may copy and distribute this program or any portion of it in
  69. compiled, executable or object code form under the terms of Paragraphs
  70. 1 and 2 above provided that you do the following:
  71.  
  72.     a) accompany it with the complete corresponding machine-readable
  73.     source code, which must be distributed under the terms of
  74.     Paragraphs 1 and 2 above; or,
  75.  
  76.     b) accompany it with a written offer, valid for at least three
  77.     years, to give any third party free (except for a nominal
  78.     shipping charge) a complete machine-readable copy of the
  79.     corresponding source code, to be distributed under the terms of
  80.     Paragraphs 1 and 2 above; or,
  81.  
  82.     c) accompany it with the information you received as to where the
  83.     corresponding source code may be obtained.  (This alternative is
  84.     allowed only for noncommercial distribution and only if you
  85.     received the program in object code or executable form alone.)
  86.  
  87. For an executable file, complete source code means all the source code for
  88. all modules it contains; but, as a special exception, it need not include
  89. source code for modules which are standard libraries that accompany the
  90. operating system on which the executable file runs.
  91.  
  92.   4. You may not copy, sublicense, distribute or transfer this program
  93. except as expressly provided under this License Agreement.  Any attempt
  94. otherwise to copy, sublicense, distribute or transfer this program is void and
  95. your rights to use the program under this License agreement shall be
  96. automatically terminated.  However, parties who have received computer
  97. software programs from you with this License Agreement will not have
  98. their licenses terminated so long as such parties remain in full compliance.
  99.  
  100.   5. If you wish to incorporate parts of this program into other free
  101. programs whose distribution conditions are different, write to the Free
  102. Software Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
  103. worked out a simple rule that can be stated here, but we will often permit
  104. this.  We will be guided by the two goals of preserving the free status of
  105. all derivatives our free software and of promoting the sharing and reuse of
  106. software.
  107.  
  108.  
  109. In other words, you are welcome to use, share and improve this program.
  110. You are forbidden to forbid anyone else to use, share and improve
  111. what you give them.   Help stamp out software-hoarding!  */
  112.  
  113. #include <ctype.h>
  114. #include <stdio.h>
  115.  
  116. #include <string.h>
  117. #include <stdlib.h>
  118. #include "dfa.h"
  119. #include "regex.h"
  120. #include "common.h"
  121.  
  122. extern errno;
  123. extern char *sys_errlist[];
  124.  
  125. #define MAX(a, b) ((a) > (b) ? (a) : (b))
  126.  
  127. /* Exit status codes. */
  128. #define MATCHES_FOUND 0        /* Exit 0 if no errors and matches found. */
  129. #define NO_MATCHES_FOUND 1    /* Exit 1 if no matches were found. */
  130. #define ERROR 2            /* Exit 2 if some error occurred. */
  131.  
  132. /* Error is set true if something awful happened. */
  133. static int error;
  134.  
  135. /* The program name for error messages. */
  136. static char *prog;
  137. void CheckForBreak( void );
  138.  
  139. /* We do all our own buffering by hand for efficiency. */
  140. static char *buffer;        /* The buffer itself, grown as needed. */
  141. static bufbytes;        /* Number of bytes in the buffer. */
  142. static size_t bufalloc;        /* Number of bytes allocated to the buffer. */
  143. static bufprev;            /* Number of bytes that have been forgotten.
  144.                    This is used to get byte offsets from the
  145.                    beginning of the file. */
  146. static bufread;            /* Number of bytes to get with each read(). */
  147.  
  148. short WeAreStopped;
  149.  
  150. static void
  151. initialize_buffer()
  152. {
  153.   bufread = 8192;
  154.   bufalloc = bufread + bufread / 2;
  155.   buffer = malloc(bufalloc);
  156.   if (! buffer)
  157.     {
  158.       emitdata( "%s: Memory exhausted (%s)\r", prog,
  159.           "Some error");
  160.       exit(ERROR);
  161.     }
  162. }
  163.  
  164. /* The current input file. */
  165. static fd;
  166. static char *filename;
  167. static eof;
  168.  
  169. /* Fill the buffer retaining the last n bytes at the beginning of the
  170.    newly filled buffer (for backward context).  Returns the number of new
  171.    bytes read from disk. */
  172. static
  173. fill_buffer_retaining(n)
  174.      int n;
  175. {
  176.   char *p, *q;
  177.   int i;
  178.  
  179.   /* See if we need to grow the buffer. */
  180.   if (bufalloc - n <= bufread)
  181.     {
  182.       while (bufalloc - n <= bufread)
  183.     {
  184.       bufalloc *= 2;
  185.       bufread *= 2;
  186.     }
  187.       buffer = realloc(buffer, bufalloc);
  188.       if (! buffer)
  189.     {
  190.       emitdata( "%s: Memory exhausted (%s)\r", prog,
  191.           "Some error");
  192.       exit(ERROR);
  193.     }
  194.     }
  195.  
  196.   bufprev += bufbytes - n;
  197.  
  198.   /* Shift stuff down. */
  199.   for (i = n, p = buffer, q = p + bufbytes - n; i--; )
  200.     *p++ = *q++;
  201.   bufbytes = n;
  202.  
  203.   if (eof)
  204.     return 0;
  205.  
  206.   /* Read in new stuff. */
  207.   i = read(fd, buffer + bufbytes, bufread);
  208.   CheckForBreak();
  209.   if (WeAreStopped) i = 0;  /* exit this thing */
  210.   if (i < 0)
  211.     {
  212.       emitdata( "%s: read on %s failed (%s)\r", prog,
  213.           filename ? filename : "<stdin>", "Some error");
  214.       error = 1;
  215.     }
  216.  
  217.   /* Kludge to pretend every nonempty file ends with a newline. */
  218.   if (i == 0 && bufbytes > 0 && buffer[bufbytes - 1] != '\r')  /* newline changed */
  219.     {
  220.       eof = i = 1;
  221.       buffer[bufbytes] = '\r';  /* newline changed */
  222.     }
  223.  
  224.   bufbytes += i;
  225.   return i;
  226. }
  227.  
  228. /* Various flags set according to the argument switches. */
  229. static trailing_context = 0;/* Lines of context to show after matches. */
  230. static leading_context = 0;    /* Lines of context to show before matches. */
  231. static byte_count = 0;        /* Precede output lines the byte count of the
  232.                    first character on the line. */
  233. static no_filenames = 0;        /* Do not display filenames. */
  234. static line_numbers = 0;        /* Precede output lines with line numbers. */
  235. static silent = 0;            /* Produce no output at all.  This switch
  236.                    is bogus, ever hear of /dev/null? */
  237. static nonmatching_lines = 0;    /* Print lines that don't match the regexp. */
  238.  
  239. static bmgexec;            /* Invoke Boyer-Moore-Gosper routines */
  240.  
  241. /* The compiled regular expression lives here. */
  242. static struct regexp reg;
  243.  
  244. /* The compiled regular expression for the backtracking matcher lives here. */
  245. static struct re_pattern_buffer regex;
  246.  
  247. /* Pointer in the buffer after the last character printed. */
  248. static char *printed_limit;
  249.  
  250. /* True when printed_limit has been artifically advanced without printing
  251.    anything. */
  252. static int printed_limit_fake;
  253.  
  254. static char prbuf[82];
  255. static prbufind = 0;
  256.  
  257. /* Print a line at the given line number, returning the number of
  258.    characters actually printed.  Matching is true if the line is to
  259.    be considered a "matching line".  This is only meaningful if
  260.    surrounding context is turned on. */
  261. static
  262. print_line(p, number, matching)
  263.      char *p;
  264.      int number;
  265.      int matching;
  266. {
  267.   int count = 0;
  268.  
  269.   if (silent)
  270.     {
  271.       do
  272.     ++count;
  273.       while (*p++ != '\r');  /* newline changed */
  274.       printed_limit_fake = 0;
  275.       printed_limit = p;
  276.       return count;
  277.     }
  278.  
  279.   if (filename && !no_filenames)
  280.     emitdata("%s%c", filename, matching ? ':' : '-');
  281.   if (byte_count)
  282.     emitdata("%d%c", p - buffer + bufprev, matching ? ':' : '-');
  283.   if (line_numbers)
  284.     emitdata("%d%c", number, matching ? ':' : '-');
  285.   do
  286.     {
  287.       ++count;
  288.       if (*p == '\r') {
  289.          prbuf[prbufind] = '\r';
  290.          prbufind++;
  291.          prbuf[prbufind] = '\0';
  292.          emitdata("%s", prbuf);
  293.          prbufind = 0;
  294.       } else {
  295.          prbuf[prbufind] = *p;
  296.          prbufind++;
  297.          if (prbufind == 80) {
  298.             prbuf[prbufind] = '\0';
  299.             emitdata("%s", prbuf);
  300.             prbufind = 0;
  301.          }
  302.       }
  303.     }
  304.   while (*p++ != '\r');  /* newline changed */
  305.   printed_limit_fake = 0;
  306.   printed_limit = p;
  307.   return count;
  308. }
  309.  
  310. /* Print matching or nonmatching lines from the current file.  Returns a
  311.    count of matching or nonmatching lines. */
  312. static
  313. grep()
  314. {
  315.   int retain = 0;        /* Number of bytes to retain on next call
  316.                    to fill_buffer_retaining(). */
  317.   char *search_limit;        /* Pointer to the character after the last
  318.                    newline in the buffer. */
  319.   char saved_char;        /* Character after the last newline. */
  320.   char *resume;            /* Pointer to where to resume search. */
  321.   int resume_index = 0;        /* Count of characters to ignore after
  322.                    refilling the buffer. */
  323.   int line_count = 1;        /* Line number. */
  324.   int try_backref;        /* Set to true if we need to verify the
  325.                    match with a backtracking matcher. */
  326.   int initial_line_count;    /* Line count at beginning of last search. */
  327.   char *match;            /* Pointer to the first character after the
  328.                    string matching the regexp. */
  329.   int match_count = 0;        /* Count of matching lines. */
  330.   char *matching_line;        /* Pointer to first character of the matching
  331.                    line, or of the first line of context to
  332.                    print if context is turned on. */
  333.   char *real_matching_line;    /* Pointer to the first character of the
  334.                    real matching line. */
  335.   char *next_line;        /* Pointer to first character of the line
  336.                    following the matching line. */
  337.   int pending_lines = 0;    /* Lines of context left over from last match
  338.                    that we have to print. */
  339.   static first_match = 1;    /* True when nothing has been printed. */
  340.   int i;
  341.   char *tmp;
  342.   char *execute();
  343.  
  344.   printed_limit_fake = 0;
  345.   
  346.   while (fill_buffer_retaining(retain) > 0)
  347.     {
  348.       /* Find the last newline in the buffer. */
  349.       search_limit = buffer + bufbytes;
  350.       while (search_limit > buffer && search_limit[-1] != '\r')  /* newline changed */
  351.     --search_limit;
  352.       if (search_limit == buffer)
  353.     {
  354.       retain = bufbytes;
  355.       continue;
  356.     }
  357.  
  358.       /* Save the character after the last newline so regexecute can write
  359.      its own sentinel newline. */
  360.       saved_char = *search_limit;
  361.  
  362.       /* Search the buffer for a match. */
  363.       printed_limit = buffer;
  364.       resume = buffer + resume_index;
  365.       initial_line_count = line_count;
  366.  
  367.       while (match = execute(®, resume, search_limit, 0, &line_count, &try_backref))
  368.     {
  369.       ++match_count;
  370.  
  371.       /* Find the beginning of the matching line. */
  372.       matching_line = match;
  373.       while (matching_line > resume && matching_line[-1] != '\r')  /* newline changed */
  374.         --matching_line;
  375.       real_matching_line = matching_line;
  376.  
  377.       /* Find the beginning of the next line. */
  378.       next_line = match;
  379.       while (next_line < search_limit && *next_line++ != '\r')  /* newline changed */
  380.         ;
  381.  
  382.       /* If a potential backreference is indicated, try it out with
  383.          a backtracking matcher to make sure the line is a match. */
  384.       if (try_backref && re_search(®ex, matching_line,
  385.                        next_line - matching_line - 1,
  386.                        0,
  387.                        next_line - matching_line - 1,
  388.                        NULL) < 0)
  389.         {
  390.           resume = next_line;
  391.           if (resume == search_limit)
  392.         break;
  393.           else
  394.         continue;
  395.         }
  396.  
  397.       /* Print leftover lines from last time.  If nonmatching_lines is
  398.          turned on, print these as if they were matching lines. */
  399.       while (resume < matching_line && pending_lines)
  400.         {
  401.           resume += print_line(resume, initial_line_count++,
  402.                    nonmatching_lines);
  403.           --pending_lines;
  404.         }
  405.  
  406.       /* Print out the matching or nonmatching lines as necessary. */
  407.       if (! nonmatching_lines)
  408.         {
  409.           /* Back up over leading context if necessary. */
  410.           for (i = leading_context; matching_line > printed_limit
  411.            && i; --i)
  412.         {
  413.           while (matching_line > printed_limit
  414.              && (--matching_line)[-1] != '\r')  /* newline changed */
  415.             ;
  416.           --line_count;
  417.         }
  418.  
  419.           /* If context is enabled, we may have to print a separator. */
  420.           if ((leading_context || trailing_context) && !silent
  421.           && !first_match && (printed_limit_fake || matching_line
  422.                       > printed_limit))
  423.         emitdata("----------\r");
  424.           first_match = 0;
  425.  
  426.           /* Print the matching line and its leading context. */
  427.           while (matching_line < real_matching_line)
  428.         matching_line += print_line(matching_line, line_count++, 0);
  429.           matching_line += print_line(matching_line, line_count++, 1);
  430.  
  431.           /* If there's trailing context, leave some lines pending until
  432.          next time. */
  433.           pending_lines = trailing_context;
  434.         }
  435.       else if (matching_line > resume)
  436.         {
  437.           char *real_resume = resume;
  438.  
  439.           /* Back up over leading context if necessary. */
  440.           for (i = leading_context; resume > printed_limit && i; --i)
  441.         {
  442.           while (resume > printed_limit && (--resume)[-1] != '\r')  /* newline changed */
  443.             ;
  444.           --initial_line_count;
  445.         }
  446.  
  447.           /* If context is enabled, we may have to print a separator. */
  448.           if ((leading_context || trailing_context) && !silent
  449.           && !first_match && (printed_limit_fake || resume
  450.                       > printed_limit))
  451.         emitdata("----------\r");
  452.           first_match = 0;
  453.  
  454.           /* Print out the presumably matching leading context. */
  455.           while (resume < real_resume)
  456.         resume += print_line(resume, initial_line_count++, 0);
  457.  
  458.           /* Print out the nonmatching lines prior to the matching line. */
  459.           while (resume < matching_line)
  460.         resume += print_line(resume, initial_line_count++, 1);
  461.  
  462.           /* Deal with trailing context. */
  463.           if (trailing_context)
  464.         {
  465.           print_line(matching_line, line_count, 0);
  466.           pending_lines = trailing_context - 1;
  467.         }
  468.  
  469.           /* Count the current line. */
  470.           ++line_count;
  471.         }
  472.       else
  473.         {
  474.           /* The line immediately after a matching line has to be printed
  475.          because it was pending. */
  476.           if (pending_lines > 0)
  477.         {
  478.           --pending_lines;
  479.           print_line(matching_line, line_count, 0);
  480.         }
  481.           ++line_count;
  482.         }
  483.  
  484.       /* Resume searching at the beginning of the next line. */
  485.       initial_line_count = line_count;
  486.       resume = next_line;
  487.  
  488.       if (resume == search_limit)
  489.         break;
  490.     }
  491.  
  492.       /* Restore the saved character. */
  493.       *search_limit = saved_char;
  494.  
  495.       if (! nonmatching_lines)
  496.     {
  497.       while (resume < search_limit && pending_lines)
  498.         {
  499.           resume += print_line(resume, initial_line_count++, 0);
  500.           --pending_lines;
  501.         }
  502.     }
  503.       else if (search_limit > resume)
  504.     {
  505.       char *initial_resume = resume;
  506.  
  507.       /* Back up over leading context if necessary. */
  508.       for (i = leading_context; resume > printed_limit && i; --i)
  509.         {
  510.           while (resume > printed_limit && (--resume)[-1] != '\r')  /* newline changed */
  511.         ;
  512.           --initial_line_count;
  513.         }
  514.  
  515.       /* If context is enabled, we may have to print a separator. */
  516.       if ((leading_context || trailing_context) && !silent
  517.           && !first_match && (printed_limit_fake || resume
  518.                   > printed_limit))
  519.         emitdata("----------\r");
  520.       first_match = 0;
  521.  
  522.       /* Print out all the nonmatching lines up to the search limit. */
  523.       while (resume < initial_resume)
  524.         resume += print_line(resume, initial_line_count++, 0);
  525.       while (resume < search_limit)
  526.         resume += print_line(resume, initial_line_count++, 1);
  527.  
  528.       pending_lines = trailing_context;
  529.       resume_index = 0;
  530.       retain = bufbytes - (search_limit - buffer);
  531.       continue;
  532.     }
  533.       
  534.       /* Save the trailing end of the buffer for possible use as leading
  535.      context in the future. */
  536.       i = leading_context;
  537.       tmp = search_limit;
  538.       while (tmp > printed_limit && i--)
  539.     while (tmp > printed_limit && (--tmp)[-1] != '\r')  /* newline changed */
  540.       ;
  541.       resume_index = search_limit - tmp;
  542.       retain = bufbytes - (tmp - buffer);
  543.       if (tmp > printed_limit)
  544.     printed_limit_fake = 1;
  545.     }
  546.  
  547.   return nonmatching_lines ? (line_count - 1) - match_count : match_count;
  548. }
  549.  
  550.  
  551.  
  552. /*
  553.    bmg_setup() and bmg_search() adapted from:
  554.      Boyer/Moore/Gosper-assisted 'egrep' search, with delta0 table as in
  555.      original paper (CACM, October, 1977).  No delta1 or delta2.  According to
  556.      experiment (Horspool, Soft. Prac. Exp., 1982), delta2 is of minimal
  557.      practical value.  However, to improve for worst case input, integrating
  558.      the improved Galil strategies (Apostolico/Giancarlo, Siam. J. Comput.,
  559.      February 1986) deserves consideration.
  560.  
  561.      James A. Woods                Copyleft (C) 1986, 1988
  562.      NASA Ames Research Center
  563. */
  564.  
  565. char *
  566. execute(r, begin, end, newline, count, try_backref)
  567.   struct regexp *r;
  568.   char *begin;
  569.   char *end;
  570.   int newline;
  571.   int *count;
  572.   int *try_backref;
  573. {
  574.   register char *p, *s;
  575.   char *match;
  576.   char *start = begin;
  577.   char save;            /* regexecute() sentinel */
  578.   int len;
  579.   char *bmg_search();
  580.  
  581.   if (!bmgexec)            /* full automaton search */
  582.     return(regexecute(r, begin, end, newline, count, try_backref));
  583.   else
  584.     {
  585.       len = end - begin; 
  586.       while ((match = bmg_search((unsigned char *) start, len)) != NULL)
  587.     {
  588.       p = match;        /* narrow search range to submatch line */
  589.       while (p > begin && *p != '\r')  /* newline changed */
  590.         p--;
  591.       s = match;
  592.       while (s < end && *s != '\r')  /* newline changed */
  593.         s++;
  594.       s++;
  595.  
  596.       save = *s;
  597.       *s = '\0';
  598.       match = regexecute(r, p, s, newline, count, try_backref);
  599.       *s = save;
  600.  
  601.       if (match != NULL)
  602.         return((char *) match);
  603.       else
  604.         {
  605.           start = s;
  606.           len = end - start;
  607.         }
  608.     }
  609.       return(NULL);
  610.     }
  611. }
  612.  
  613. #include <ctype.h>
  614. int        delta0[256];
  615. unsigned char   cmap[256];        /* (un)folded characters */
  616. unsigned char    pattern[5000];
  617. int        patlen;
  618.  
  619. char *
  620. bmg_search(buffer, buflen)
  621.   unsigned char *buffer;
  622.   int buflen;
  623. {
  624.   register unsigned char *k, *strend, *s, *buflim;
  625.   register int t;
  626.   int j;
  627.  
  628.   if (patlen > buflen)
  629.     return NULL;
  630.  
  631.   buflim = buffer + buflen;
  632.   if (buflen > patlen * 4)
  633.     strend = buflim - patlen * 4;
  634.   else
  635.     strend = buffer;
  636.  
  637.   s = buffer;
  638.   k = buffer + patlen - 1;
  639.  
  640.   for (;;)
  641.     {
  642.       /* The dreaded inner loop, revisited. */
  643.       while (k < strend && (t = delta0[*k]))
  644.     {
  645.       k += t;
  646.       k += delta0[*k];
  647.       k += delta0[*k];
  648.     }
  649.       while (k < buflim && delta0[*k])
  650.     ++k;
  651.       if (k == buflim)
  652.     break;
  653.     
  654.       j = patlen - 1;
  655.       s = k;
  656.       while (--j >= 0 && cmap[*--s] == pattern[j])
  657.     ;
  658.       /* 
  659.     delta-less shortcut for literati, but 
  660.     short shrift for genetic engineers.
  661.       */
  662.       if (j >= 0)
  663.     k++;
  664.       else         /* submatch */
  665.     return ((char *)k);
  666.     }
  667.   return(NULL);
  668. }
  669.  
  670. bmg_setup(pat, folded)            /* compute "boyer-moore" delta table */
  671.   char *pat;
  672.   int folded;
  673. {                    /* ... HAKMEM lives ... */
  674.   int j;
  675.  
  676.   patlen = strlen(pat);
  677.  
  678.   if (folded)                 /* fold case while saving pattern */
  679.     for (j = 0; j < patlen; j++) 
  680.       pattern[j] = (isupper((int) pat[j]) ?
  681.     (char) tolower((int) pat[j]) : pat[j]);
  682.   else
  683.       memcpy(pattern, pat, patlen);
  684.  
  685.   for (j = 0; j < 256; j++)
  686.     {
  687.       delta0[j] = patlen;
  688.       cmap[j] = (char) j;        /* could be done at compile time */
  689.     }
  690.   for (j = 0; j < patlen - 1; j++)
  691.     delta0[pattern[j]] = patlen - j - 1;
  692.   delta0[pattern[patlen - 1]] = 0;
  693.  
  694.   if (folded)
  695.     {
  696.       for (j = 0; j < patlen - 1; j++)
  697.     if (islower((int) pattern[j]))
  698.       delta0[toupper((int) pattern[j])] = patlen - j - 1;
  699.     if (islower((int) pattern[patlen - 1]))
  700.       delta0[toupper((int) pattern[patlen - 1])] = 0;
  701.       for (j = 'A'; j <= 'Z'; j++)
  702.     cmap[j] = (char) tolower((int) j);
  703.     }
  704. }
  705.  
  706.  
  707. #include <stdarg.h>
  708. #include <ctype.h>
  709. #include <memory.h>
  710. #include <Menus.h>
  711. #include <SysEqu.h>
  712. #include <pascal.h>
  713. #include <Dialogs.h>
  714. #include <Menus.h>
  715. #include <OSUtils.h>
  716. #include <StandardFile.h>
  717. #include "TransSkel.h"
  718. #include "TransDisplay.h"
  719. #include "gff.h"
  720.  
  721. extern void *alloca();
  722.  
  723. #define FileMenuID 500
  724. #define LSTWIND 500
  725. #define AboutID 500
  726. #define MessageID 502
  727. #define ARGDLOG 503
  728. #define HELPID 504
  729. #define PFolderID 200
  730. #define ErrorID 501
  731. #define ARGSEARCH 1
  732. #define ARGCANCEL 2
  733. #define ARGPFOLDER 3
  734. #define ARGPFILE 4
  735. #define ARGHELP 5
  736. #define ARGREGEXP 8
  737. #define ARGFILEPAT 9
  738. #define ARGSHOWFNAME 10
  739. #define ARGIGNCASE 11
  740. #define ARGONLYFNAME 12
  741. #define ARGSHOWLNUM 13
  742. #define ARGSHSEARCH 14
  743. #define ARGNONMATCH 15
  744. #define ARGTRAILCONT 16
  745. #define ARGLEADCONT 18
  746. #define ARGSHOWBYTE 20
  747. #define ARGCOUNT 21
  748. #define ARGWHWORD 22
  749. #define ARGWHLINE 23
  750. #define ARGOUTFILE 24
  751.  
  752. #define ON 1
  753. #define OFF 0
  754.  
  755. static WindowPtr    theWindow;
  756. static short AllDone = false;
  757. static DialogPtr ArgDialog;
  758. static int argc;
  759. static char **argv;
  760. static int ignore_case = 1;        /* Compile the regexp to ignore case. */
  761. static char regexp_buf[256];        /* The regular expression. */
  762. static int regexp_len;            /* Length of the regular expression. */
  763. static int count_lines = 0;        /* Show only count of matching lines. */
  764. static int list_files = 0;        /* Show only names of matching files. */
  765. static int whole_word = 0;        /* regexp much match a word only. */
  766. static int whole_line = 0;        /* matching only whole lines. */
  767. static int line_count = 0;        /* Count of matching lines for a file. */
  768. static int use_outfile = 0;     /* Output to file */
  769. static SFReply tmpFile;
  770. static int ShowSearchedFiles = 0;  /* Display files searched */
  771. static char file_pattern[256];
  772. static char translate[_NOTCHAR]; /* Translate table for case conversion
  773.                    (needed by the backtracking matcher). */
  774. static SkelStarted = false;
  775. static WDPBRec theWDPBRec;               
  776. static KeyMap km;
  777. static int OutFileOpen;
  778. static FILE *fp;
  779.  
  780. void CheckForBreak( void )
  781. {
  782.    int i;
  783.    
  784.    GetKeys(km);
  785.    if (km[1] == 8421376L)  /* Seems to be fan-period */
  786.         WeAreStopped = true;
  787. }
  788.  
  789.  
  790. void AbEnd(Str255 p)
  791. {
  792.    ParamText(p, "\p", "\p", "\p");
  793.    Alert(ErrorID, 0L);
  794.    if (OutFileOpen) {
  795.          fclose(fp);
  796.          OutFileOpen = false;
  797.    }
  798.    /* Close old WD if needed */
  799.    if (theWDPBRec.ioWDProcID != 0) PBCloseWD(&theWDPBRec, false);
  800.    if (SkelStarted) {
  801.       SkelClobber();
  802.       SkelWhoa();
  803.    }
  804.    exit(1);
  805. }
  806.  
  807.  
  808. void Message(Str255 p)
  809. {
  810.    ParamText(p, "\p", "\p", "\p");
  811.    Alert(MessageID, 0L);
  812. }
  813.  
  814. static void SetArgItem(DialogPtr ArgDialog, short item, short value)
  815. {
  816.    short itemType;
  817.    Rect itemRect;
  818.    Handle itemHandle;
  819.    
  820.    GetDItem(ArgDialog, item, &itemType, &itemHandle, &itemRect);
  821.    SetCtlValue((ControlHandle) itemHandle, value);
  822. }
  823.  
  824. pascal Boolean FoldersOnly(p)
  825.     ParmBlkPtr p;
  826. {
  827.     /* Normally, folders are ALWAYS shown, and aren't even passed to        */
  828.     /* this file filter for judgement. Under such circumstances, it is        */
  829.     /* only necessary to blindly return TRUE (allow no files whatsoever).    */
  830.     /* However, Standard File is not documented in such a manner, and        */
  831.     /* this feature may not be TRUE in the future. Therefore, we DO check    */
  832.     /* to see if the entry passed to us describes a file or a directory.    */
  833.  
  834.     if ((p->fileParam.ioFlAttrib & 0x10) != 0)
  835.         return(false);
  836.     return(true);
  837. }
  838.  
  839.  
  840. static int GetSearchFolder(void)
  841. {
  842.     SFReply theDir;
  843.     Point p;
  844.     char *fname;
  845.     OSType ftype = 'TEXT';  /* Value does not matter */
  846.     OSErr err;
  847.    
  848.     p.h = 100; p.v = 100;
  849.     gff_Get(&p, "\pPick the search folder", FoldersOnly, 0, &ftype,
  850.             &theDir, PFolderID);
  851.             
  852.      /* SFGetFile(p,"\p",0L,1,&ftype,0L,&theDir); */
  853.             
  854.     if (theDir.good) {
  855.        /* Close old WD if needed */
  856.        if (theWDPBRec.ioWDProcID != 0) {
  857.           if ((err = PBCloseWD(&theWDPBRec, false)) != noErr)
  858.              AbEnd("\pCould not close working directory!");
  859.        }
  860.        theWDPBRec.ioCompletion = 0L;
  861.        theWDPBRec.ioNamePtr = 0L;
  862.        theWDPBRec.ioVRefNum = theDir.vRefNum;
  863.        theWDPBRec.ioWDProcID = 'gReP';
  864.        theWDPBRec.ioWDDirID = theDir.fType;
  865.        if ((err = PBOpenWD(&theWDPBRec, false)) != noErr) {
  866.           AbEnd("\pCould not open working directory!");
  867.        }
  868.       
  869.        SetVol(0L, theWDPBRec.ioVRefNum); /* Set default vol to wd */
  870.        return true;
  871.     } else
  872.        return false;      
  873. }
  874.  
  875.  
  876. /* Toggle value of checkbox.  Return new value. */
  877. static short ToggleArgItem(DialogPtr ArgDialog, short item)
  878. {
  879.    short itemType;
  880.    Rect itemRect;
  881.    Handle itemHandle;
  882.    short value;
  883.    
  884.    GetDItem(ArgDialog, item, &itemType, &itemHandle, &itemRect);
  885.    value = GetCtlValue((ControlHandle) itemHandle);
  886.    value = 1 - value;
  887.    SetCtlValue((ControlHandle) itemHandle, value);
  888.    return value;
  889. }
  890.  
  891.  
  892. static void SetArgDlog(void)
  893. {
  894.    short itemType;
  895.    Rect itemRect;
  896.    Handle itemHandle;
  897.    char NumBuf[32];
  898.  
  899.    GetDItem(ArgDialog, ARGREGEXP, &itemType, &itemHandle, &itemRect);
  900.    SetIText(itemHandle, CtoPstr(regexp_buf));
  901.  
  902.    GetDItem(ArgDialog, ARGFILEPAT, &itemType, &itemHandle, &itemRect);
  903.    SetIText(itemHandle, CtoPstr(file_pattern));
  904.    
  905.    sprintf(NumBuf, "%d", leading_context);
  906.    GetDItem(ArgDialog, ARGLEADCONT, &itemType, &itemHandle, &itemRect);
  907.    SetIText(itemHandle, CtoPstr(NumBuf));
  908.  
  909.    sprintf(NumBuf, "%d", trailing_context);
  910.    GetDItem(ArgDialog, ARGTRAILCONT, &itemType, &itemHandle, &itemRect);
  911.    SetIText(itemHandle, CtoPstr(NumBuf));
  912.    
  913.    SetArgItem(ArgDialog, ARGSHOWFNAME, 1 - no_filenames);
  914.    SetArgItem(ArgDialog, ARGIGNCASE, ignore_case);
  915.    SetArgItem(ArgDialog, ARGONLYFNAME, list_files);
  916.    SetArgItem(ArgDialog, ARGSHOWLNUM, line_numbers);
  917.    SetArgItem(ArgDialog, ARGSHSEARCH, ShowSearchedFiles);
  918.    SetArgItem(ArgDialog, ARGNONMATCH, nonmatching_lines);
  919.    SetArgItem(ArgDialog, ARGSHOWBYTE, byte_count);
  920.    SetArgItem(ArgDialog, ARGCOUNT, count_lines);
  921.    SetArgItem(ArgDialog, ARGWHWORD, whole_word);
  922.    SetArgItem(ArgDialog, ARGWHLINE, whole_line);
  923.    SetArgItem(ArgDialog, ARGOUTFILE, use_outfile);
  924. }
  925.  
  926.  
  927. /* Check to see if the user's option choices are sane */
  928. int CheckChoices(DialogPtr ArgDialog)
  929. {
  930.    short itemType;
  931.    Rect itemRect;
  932.    Handle itemHandle;
  933.    void glob_filelist();
  934.    char NumBuf[32];
  935.    
  936.    GetDItem(ArgDialog, ARGREGEXP, &itemType, &itemHandle, &itemRect);
  937.    GetIText(itemHandle, (void *) regexp_buf);
  938.    (void) PtoCstr((unsigned char *) regexp_buf);
  939.    regexp_len = strlen(regexp_buf);
  940.    if (regexp_len == 0) {
  941.       Message("\pYou must enter a regexp!");
  942.       return false;
  943.    }
  944.    
  945.    GetDItem(ArgDialog, ARGFILEPAT, &itemType, &itemHandle, &itemRect);
  946.    GetIText(itemHandle, (void *) file_pattern);
  947.    (void) PtoCstr((unsigned char *) file_pattern);
  948.    glob_filelist(file_pattern, &argc, &argv);
  949.    if (argc == 0) {
  950.       Message("\pFilename pattern matches nothing!");
  951.       return false;
  952.    }
  953.    
  954.    GetDItem(ArgDialog, ARGLEADCONT, &itemType, &itemHandle, &itemRect);
  955.    GetIText(itemHandle, (void *) NumBuf);
  956.    (void) PtoCstr((unsigned char *) NumBuf);
  957.    if (sscanf(NumBuf, "%d", &leading_context) != 1) {
  958.       Message("\pLeading context must be a non-negative integer");
  959.       return false;
  960.    }
  961.    
  962.    GetDItem(ArgDialog, ARGTRAILCONT, &itemType, &itemHandle, &itemRect);
  963.    GetIText(itemHandle, (void *) NumBuf);
  964.    (void) PtoCstr((unsigned char *) NumBuf);
  965.    if (sscanf(NumBuf, "%d", &trailing_context) != 1) {
  966.       Message("\pTrailing context must be a non-negative integer");
  967.       return false;
  968.    }
  969.    
  970.    if (leading_context < 0 || trailing_context < 0) {
  971.       Message("\pContext lines must be a non-negative integer");
  972.       return false;
  973.    }
  974.       
  975.    return true;
  976. }
  977.  
  978. static Point    dlogWhere = { 70, 100 };
  979.  
  980. static int GetOutFile (void)
  981. {
  982.    FInfo    fndrInfo;    /* finder info */
  983.  
  984.    SFPutFile (dlogWhere, "\pSave file as:", "\puntitled", nil, &tmpFile);
  985.    if (!tmpFile.good) {
  986.       return false;
  987.    }
  988.  
  989.    /* Delete if exists */    
  990.    if (GetFInfo (tmpFile.fName, tmpFile.vRefNum, &fndrInfo) == noErr)
  991.       if (FSDelete(tmpFile.fName, tmpFile.vRefNum) != noErr) {
  992.          Message("\pCannot Delete File");
  993.          return false;
  994.       }
  995.      
  996.  
  997.    if (Create (tmpFile.fName,tmpFile.vRefNum,'ttxt','TEXT') != noErr) {
  998.       Message("\pCan't Create File");
  999.       return false;
  1000.    }
  1001.    
  1002.    return true;
  1003.     
  1004. }
  1005.  
  1006. short DoArgDlog (void)
  1007. {
  1008.    short int itemHit, dialogDone = false;
  1009.    short itemType, Cancel = false;
  1010.    Rect itemRect;
  1011.    Handle itemHandle;
  1012.    int c;
  1013.    
  1014.    ArgDialog = GetNewDialog(ARGDLOG, 0L, (WindowPtr) -1);
  1015.    SetArgDlog();
  1016.  
  1017.    ShowWindow(ArgDialog);
  1018.     
  1019.    while (dialogDone == false) {
  1020.    
  1021.       ModalDialog(0L, &itemHit);
  1022.       switch (itemHit)
  1023.       {
  1024.          case ARGCANCEL:
  1025.             Cancel = true;
  1026.             dialogDone = true;
  1027.             (void) PtoCstr((unsigned char *) regexp_buf);
  1028.             (void) PtoCstr((unsigned char *) file_pattern);
  1029.             break;
  1030.          case ARGSEARCH:
  1031.             if (CheckChoices(ArgDialog)) dialogDone = true;
  1032.             break;
  1033.          case ARGPFOLDER:
  1034.             (void) GetSearchFolder();
  1035.             break;
  1036.          case ARGPFILE:
  1037.             Message("\pNot implemented, use file pattern");
  1038.             break;          
  1039.          case ARGHELP:
  1040.             Alert(HELPID, 0L);
  1041.             break;
  1042.          case ARGSHOWFNAME:
  1043.             no_filenames = 1 - ToggleArgItem(ArgDialog, ARGSHOWFNAME);
  1044.             break;
  1045.          case ARGIGNCASE:
  1046.             ignore_case = ToggleArgItem(ArgDialog, ARGIGNCASE);
  1047.             break;
  1048.          case ARGONLYFNAME:
  1049.             list_files = ToggleArgItem(ArgDialog, ARGONLYFNAME);
  1050.             silent = list_files;
  1051.             break;
  1052.          case ARGSHOWLNUM:
  1053.             line_numbers = ToggleArgItem(ArgDialog, ARGSHOWLNUM);
  1054.             break;
  1055.          case ARGSHSEARCH:
  1056.             ShowSearchedFiles = ToggleArgItem(ArgDialog, ARGSHSEARCH);
  1057.             break;
  1058.          case ARGNONMATCH:
  1059.             nonmatching_lines = ToggleArgItem(ArgDialog, ARGNONMATCH);
  1060.          case ARGSHOWBYTE:
  1061.             byte_count = ToggleArgItem(ArgDialog, ARGSHOWBYTE);
  1062.             break;
  1063.          case ARGCOUNT:
  1064.             count_lines = ToggleArgItem(ArgDialog, ARGCOUNT);
  1065.             silent = count_lines;
  1066.             break;
  1067.          case ARGWHWORD:
  1068.             whole_word = ToggleArgItem(ArgDialog, ARGWHWORD);
  1069.             break;
  1070.          case ARGWHLINE:
  1071.             whole_line = ToggleArgItem(ArgDialog, ARGWHLINE);
  1072.             break;
  1073.          case ARGOUTFILE:
  1074.             use_outfile = ToggleArgItem(ArgDialog, ARGOUTFILE);
  1075.             break;
  1076.       } /* switch */
  1077.       
  1078.    } /* while */
  1079.    
  1080.    HideWindow(ArgDialog);
  1081.    DisposDialog (ArgDialog);
  1082.    
  1083.    if (Cancel) return false;
  1084.    
  1085.    if (ignore_case) {
  1086.       for (c = 0; c < _NOTCHAR; c++)
  1087.          if (isupper(c))
  1088.             translate[c] = tolower(c);
  1089.          else
  1090.             translate[c] = c;
  1091.          regex.translate = translate;
  1092.    }
  1093.    
  1094.    return true;
  1095.   
  1096. } /* DoArgDlog */
  1097.  
  1098.  
  1099. static char edatabuf[256];
  1100.  
  1101. void emitdata(char *pat, ...)
  1102. {
  1103.    va_list ap;
  1104.    
  1105.    va_start(ap, pat);
  1106.    vsprintf(edatabuf, pat, ap);
  1107.    if (OutFileOpen)
  1108.       fprintf(fp, "%s", edatabuf);
  1109.    else
  1110.          DisplayText(edatabuf, strlen(edatabuf));
  1111.    va_end(ap);
  1112. }
  1113.  
  1114.  
  1115. static void DoAbout(void)
  1116. {
  1117.    Alert(AboutID, 0L);
  1118. }
  1119.  
  1120.  
  1121. /* Needed by the regexp routines.  This could be fancier, especially when
  1122.    dealing with parallel regexps in files. */
  1123. void
  1124. regerror(s)
  1125.      char *s;
  1126. {
  1127.   AbEnd((void *)CtoPstr((void *)s));
  1128. }
  1129.  
  1130.  
  1131.  
  1132. void ExecuteGrep(void)
  1133. {
  1134.   char *the_regexp;
  1135.   int i;
  1136.  
  1137.   int matches_found = 0;    /* True if matches were found. */
  1138.   char *regex_errmesg;        /* Error message from regex routines. */
  1139.  
  1140.   WeAreStopped = false;
  1141.  
  1142.   if (argc == 1) no_filenames = 1;
  1143.  
  1144.   the_regexp = regexp_buf;
  1145.  
  1146.   /* Set the syntax depending on whether we are EGREP or not. */
  1147. #ifdef EGREP
  1148.   regsyntax(RE_SYNTAX_EGREP, ignore_case);
  1149.   re_set_syntax(RE_SYNTAX_EGREP);
  1150. #else
  1151.   regsyntax(RE_SYNTAX_GREP, ignore_case);
  1152.   re_set_syntax(RE_SYNTAX_GREP);
  1153. #endif
  1154.  
  1155.   if (regexp_len == 0) emitdata("Error: no regexp!\r");  /* should die! */
  1156.   
  1157.   if (whole_word || whole_line)
  1158.     {
  1159.       char *n = malloc(regexp_len + 8);
  1160.       int i = 0;
  1161.  
  1162.       if (whole_line)
  1163.     n[i++] = '^';
  1164.       else
  1165.     n[i++] = '\\', n[i++] = '<';
  1166. #ifndef EGREP
  1167.       n[i++] = '\\';
  1168. #endif
  1169.       n[i++] = '(';
  1170.       memcpy(n + i, the_regexp, regexp_len);
  1171.       i += regexp_len;
  1172. #ifndef EGREP
  1173.       n[i++] = '\\';
  1174. #endif
  1175.       n[i++] = ')';
  1176.       if (whole_line)
  1177.     n[i++] = '$';
  1178.       else
  1179.     n[i++] = '\\', n[i++] = '>';
  1180.       the_regexp = n;
  1181.       regexp_len = i;
  1182.     }
  1183.  
  1184.   regcompile(the_regexp, regexp_len, ®, 1);
  1185.   
  1186.   if (regex_errmesg = re_compile_pattern(the_regexp, regexp_len, ®ex)) {
  1187.     Message("\pSyntax error in regular expression");
  1188.     goto GiveUp;
  1189.   }
  1190.   
  1191.   /*
  1192.     Find the longest metacharacter-free string which must occur in the
  1193.     regexpr, before short-circuiting regexecute() with Boyer-Moore-Gosper.
  1194.     (Conjecture:  The problem in general is NP-complete.)  If there is no
  1195.     such string (like for many alternations), then default to full automaton
  1196.     search.  regmust() code and heuristics [see dfa.c] courtesy
  1197.     Arthur David Olson.
  1198.     */
  1199.   if (line_numbers == 0 && nonmatching_lines == 0)
  1200.     {
  1201.       if (reg.mustn == 0 || reg.mustn == MUST_MAX ||
  1202.         strchr(reg.must, '\0') != reg.must + reg.mustn)
  1203.     bmgexec = 0;
  1204.       else
  1205.     {
  1206.       reg.must[reg.mustn] = '\0';
  1207.       if (getenv("MUSTDEBUG") != NULL)
  1208.         (void) emitdata("must have: \"%s\"\r", reg.must);
  1209.       bmg_setup(reg.must, ignore_case);
  1210.       bmgexec = 1;
  1211.     }
  1212.     }
  1213.   
  1214.   initialize_buffer();
  1215.   
  1216.   DisplayString("\p\r  *** Press Command-period to stop the search  ***\r\r");
  1217.  
  1218.   for (i=0; i<argc && !WeAreStopped; i++) {
  1219.     bufprev = eof = 0;
  1220.     filename = argv[i];
  1221.     if (ShowSearchedFiles) emitdata("    ...  Searching %s\r", filename);
  1222.     fd = open(filename, 0, 0);
  1223.     if (fd < 0) {
  1224.       emitdata( "%s: %s: %s\r", prog, filename, "Could not open file");
  1225.       error = 1;
  1226.       continue;
  1227.     }
  1228.     if (line_count = grep()) matches_found = 1;
  1229.     close(fd);
  1230.     if (count_lines)
  1231.       if (!no_filenames)
  1232.          emitdata("%s:%d\r", filename, line_count);
  1233.       else
  1234.          emitdata("%d\r", line_count);
  1235.       else if (list_files && line_count)
  1236.         emitdata("%s\r", filename);
  1237.   } /* for */
  1238.   
  1239.   /* Cut down on the memory leaks */
  1240. GiveUp:
  1241.   (void) alloca(0L);
  1242.   for (i=0; i<argc; i++) free(argv[i]);
  1243.   free(argv);
  1244.   
  1245. }
  1246.  
  1247.  
  1248. void DoGrep(void)
  1249. {
  1250.    short OkToGo;
  1251.    short int tmpRefNum;
  1252.    unsigned char tmp[256];
  1253.    char *fname;
  1254.    
  1255.    OkToGo = DoArgDlog();
  1256.    
  1257.    if (OkToGo) {
  1258.       if (use_outfile) {
  1259.          if (!GetOutFile()) return;
  1260.          GetVol(tmp, &tmpRefNum);
  1261.          SetVol(0L, tmpFile.vRefNum);
  1262.          fname = PtoCstr(tmpFile.fName);
  1263.          if ((fp = fopen(fname, "a")) == NULL) 
  1264.             AbEnd("\pCould not open output file");
  1265.          OutFileOpen = true;
  1266.          SetVol(tmp, tmpRefNum);
  1267.          DisplayString("\pWriting to output file");
  1268.       }    
  1269.       ExecuteGrep();
  1270.       if (OutFileOpen) {
  1271.          fclose(fp);
  1272.          OutFileOpen = false;
  1273.       }
  1274.    }
  1275. }
  1276.  
  1277.  
  1278. static void DoFileMenu (Integer item)
  1279. {
  1280.    OSErr err;
  1281.    
  1282.    switch (item)
  1283.    {
  1284.       case 1:
  1285.          DoGrep();
  1286.          break;
  1287.       case 3:
  1288.           /* Close old WD if needed */
  1289.           if (theWDPBRec.ioWDProcID != 0) {
  1290.              if ((err = PBCloseWD(&theWDPBRec, false)) != noErr)
  1291.                 AbEnd("\pCould not close working directory!");
  1292.          }
  1293.          SkelRmveWind(theWindow);
  1294.          SkelWhoa (); /* tell SkelMain to quit */
  1295.          break;
  1296.    }
  1297. }
  1298.  
  1299.  
  1300.  
  1301. main()
  1302. {
  1303.  
  1304.   int ensureStack=48000;
  1305.   MenuHandle    MenuH;
  1306.   
  1307.   file_pattern[0] = '\0';  /* Init to null string */
  1308.   regexp_buf[0] = '\0';
  1309.   
  1310.   theWDPBRec.ioWDProcID = 0;  /* If changes, we know WD has been opened */
  1311.   
  1312.   if (*(long *)CurStackBase - *(long *)ApplLimit < ensureStack)
  1313.     SetApplLimit((Ptr)(*(long *)CurStackBase - ensureStack));
  1314.   MaxApplZone();
  1315.   
  1316.   SkelInit (6, nil);
  1317.   SkelApple ("\pAbout egrep...", DoAbout);
  1318.   
  1319.   MenuH = GetMenu (FileMenuID);    
  1320.   (void) SkelMenu (MenuH, DoFileMenu, nil, false);
  1321.   
  1322.   theWindow = GetNewDWindow(LSTWIND, nil);
  1323.  
  1324.   DoGrep();
  1325.  
  1326.   SkelStarted = true;
  1327.   SkelMain ();                    /* loop 'til Quit selected */
  1328.   SkelClobber ();                    /* clean up */
  1329. }
  1330.